import io
import logging

from django.contrib.auth.models import User
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connection
from fdfs_client.client import get_tracker_conf, Fdfs_client
from rest_framework.decorators import action
from rest_framework.mixins import RetrieveModelMixin, ListModelMixin, CreateModelMixin, UpdateModelMixin, \
    DestroyModelMixin
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet, ReadOnlyModelViewSet
from rest_framework.status import HTTP_404_NOT_FOUND, HTTP_400_BAD_REQUEST, HTTP_500_INTERNAL_SERVER_ERROR
from rest_framework_extensions.cache.mixins import CacheResponseMixin

from config.dbs.redisConfig import LOGIN_KEY_TEMPLATE, EXPIRE_TIME
from config.fastdfsConfig import FASTDFS_SERVER_DOMAIN
from users.models import UserDetail, Area, Address
from users.serializers import UserSerializer, UserDetailSerializer, AreaSerializer, AreaSonSerializer, AddressSerializer
from users.utils import ActiveUserPermission
from utils.permission import create_user_auto, update_user_auto, destroy_user_auto
from utils.verifyUtil import ImageVerify

# tracker server的实例化
tracker_path = get_tracker_conf('utils/fastdfs/client.conf')
client = Fdfs_client(tracker_path)

# 导入日志器
logger = logging.getLogger(__name__)


# Create your views here.
class ImageVerifyView(View):
    def get(self, request, uuid):
        """获取图片验证码"""
        imageVerify = ImageVerify()  # 实例化类
        # image是一个对象
        image, code = imageVerify.verify_code()

        bytes_io = io.BytesIO()  # 创建一个字节流
        image.save(bytes_io, format='PNG')
        image_bytes = bytes_io.getvalue()

        # 将客户端标识UUID作为key，将验证码答案作为value
        cache = get_redis_connection(alias='verify_codes')  # 得到缓存对象，从redis连接池中拿一个redis，名字跟配置的名字一样
        cache.set(LOGIN_KEY_TEMPLATE % uuid, code, EXPIRE_TIME)  # 传 键、值、过期时间，存入答案

        return HttpResponse(image_bytes, content_type='image/png')


class UserViewSet(RetrieveModelMixin, GenericViewSet):  # 用户查询 修改个人信息
    permission_classes = [ActiveUserPermission]
    queryset = User.objects.filter(is_active=1)
    serializer_class = UserSerializer

    def update(self, request, pk):  # 用户详情查询及更新
        # user 表更新
        # 获取user
        try:
            user = User.objects.get(id=pk)
        except User.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # 拿到传入过来的数据 反序列化 实例化序列化器传什么参数？
        user_serializer = self.get_serializer(user, data=request.data)
        # 校验
        user_serializer.is_valid(raise_exception=True)
        # 更新
        user_serializer.save()

        # detail表更新
        # 判断该用户是否有详情信息,无论修改还是保存都要校验，序列化对象(instance)
        # user.userdetail(模型类小写）,一对一查询
        user_detail = request.data['userdetail']
        if hasattr(user, 'userdetail'):
            # 如果有，则获取到该用户的详情信息，然后save修改
            user_detail_serializer = UserDetailSerializer(user.userdetail, data=user_detail)  # 第一个是instance
        # 如果没有，则创建一个详情信息，然后save保存
        else:
            user_detail_serializer = UserDetailSerializer(data=user_detail)
        user_detail_serializer.is_valid(raise_exception=True)
        user_detail_serializer.save()

        user_serializer.data['userdetail'] = user_detail_serializer.data
        return Response(user_serializer.data)

    # method不加中括号会报错405，method参数的类型错误 头像更改
    @action(methods=['post'], detail=True)  # detail为true就会自动生成一个pk参数，我们通过pk获取
    def avatar(self, request, pk):  # /users/users/<pk>/avatar
        # 获取用户
        try:
            user = User.objects.get(id=pk)
        except User.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # 获取文件数据 校验文件格式等
        files = request.FILES.get('file')  # 获取文件的方法
        if not files or files.content_type not in ('image/jpeg', 'image/png', 'image/gif'):
            return Response(status=HTTP_400_BAD_REQUEST)

        # 上传到FastDFS文件系统中
        try:
            image_ext_name = files.name.split('.')[-1]  # 获得文件后缀名
        except Exception as e:  # 如果文件后缀名有.,就设置默认后缀名
            image_ext_name = 'png'

        try:  # 上传到 storage
            upload_res = client.upload_by_buffer(files.read(), file_ext_name=image_ext_name)  # 通过文件字节上传,指定文件格式

        # 这里如果出了问题，就是server出错了，需要把问题存入日志器
        except Exception as e:  # 这里的日志器是django的
            logger.error('图片上传出现错误：{}'.format(e))
            return Response(status=HTTP_500_INTERNAL_SERVER_ERROR)
        else:
            if upload_res.get('Status') != 'Upload successed.':
                return Response({'msg': '图片上传失败，请稍后重试或联系管理员'}, status=HTTP_500_INTERNAL_SERVER_ERROR)
            else:
                image_name = upload_res.get('Remote file_id').decode()
                image_url = FASTDFS_SERVER_DOMAIN + image_name

                # 将访问地址存入到数据库中 详情表
                # 判断是否有详情表
                if hasattr(user, 'userdetail'):
                    # 有 修改头像
                    user_detail = user.userdetail
                    user_detail.avatrar = image_url
                    user_detail.save()
                else:  # 没有 创建
                    UserDetail.objects.create(avatrar=image_url, user=user)

                return Response({'data': image_url})

    @action(methods=['put'], detail=True)  # 修改密码
    def password(self, request, pk):
        try:
            user = User.objects.get(id=pk)
        except User.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)

        password = request.data['password']
        new_password = request.data['new_password']
        # 存入的密码是编码后的，所以要用check_password方法进行校验
        if user.check_password(password):
            user.set_password(new_password)
            user.save()
            return Response({'msg': '修改成功'})

        return Response(status=HTTP_400_BAD_REQUEST)


#  这里CacheResponseMixin一定要放在最前面，因为drf缓存源码是调用第一个父类的list方法
class AreaViewSet(CacheResponseMixin, ReadOnlyModelViewSet):  # 省市区视图
    def get_queryset(self):  # 重写get_queryset方法
        if self.action == 'list':  # 如果是查多条行为
            return Area.objects.filter(parent=None)
        else:  # Retrieve行为走这里，要传一个pk，指定省份
            return Area.objects.all()

    def get_serializer_class(self):
        if self.action == 'list':
            return AreaSerializer
        else:  # Retrieve行为走这里，要传一个pk，指定省份
            return AreaSonSerializer

    permission_classes = [IsAuthenticated]


# 收货地址视图
class AddressViewSet(GenericViewSet, CreateModelMixin, ListModelMixin, UpdateModelMixin, DestroyModelMixin):
    serializer_class = AddressSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):  # self里面有request对象
        # 只能查到自己的收货地址
        return self.request.user.address_set.filter(is_delete=False)

    @create_user_auto
    def create(self, request, *args, **kwargs):
        # queryset查询集是从数据库中获取的对象集合
        # 这里的count方法是django中的，可以返回查询集的长度
        if 10 <= self.get_queryset().count():
            return Response({'detail': '收货地址数量上限'}, status=HTTP_400_BAD_REQUEST)
        return CreateModelMixin.create(self, request, *args, **kwargs)

    @update_user_auto(Address)
    def update(self, request, *args, **kwargs):
        return UpdateModelMixin.update(self, request, *args, **kwargs)

    @destroy_user_auto(Address)
    def destroy(self, request, *args, **kwargs):
        return DestroyModelMixin.destroy(self, request, *args, **kwargs)
